/**
 * @file   testStarTree.cpp
 * @author wolfhead <wolfhead@wolfhead-laptop>
 * @date   Sat Dec 31 16:41:13 2011
 * 
 * @brief  
 * 
 * 
 */
#include "gtest/gtest.h"
#include <acapella/string/string_util.hpp>
#include <acapella/star_tree.hpp>

using namespace acapella;

namespace acapella
{


template<>
class NodeOperation<std::string>
{
public:

    static bool Compare(const std::string& lho, const std::string& rho)
    {
        return lho > rho;
    }

    template<typename IteratorType>
    static std::string Aggregate(const IteratorType& itBegin, const IteratorType& itEnd)
    {
        size_t sum = 0;
            
        for (IteratorType it = itBegin; it != itEnd; ++it)
        {
            sum += acapella::StringUtil::ToValue<size_t>(*it);
        }
        return acapella::StringUtil::ToString(sum);
    }

    template<typename IteratorType>
    static std::string Average(const IteratorType& itBegin, const IteratorType& itEnd)
    {
        size_t count = 0;
        size_t sum = 0;
            
        for (IteratorType it = itBegin; it != itEnd; ++it)
        {
            ++count;
            sum += acapella::StringUtil::ToValue<size_t>(*it);
        }
        if (count == 0)
        {
            return "0";
        }

        return acapella::StringUtil::ToString(sum / count);
    }
};

}

typedef StarTree<std::string, std::string, std::string> Tree;

int BuildTestTree(Tree * & pTree)
{
    const char * keys[] = {
        "1",
        "2",
        "3",
        "NULL",
        "*"
    };

    std::vector<std::string> fieldList;
    fieldList.push_back("f1");
    fieldList.push_back("f2");
    fieldList.push_back("f3");

    std::string defaultKey = "*";

    pTree = new Tree(fieldList, defaultKey, "NULL", "");

    std::map<std::string, std::string> param;
    int result = 0;

    size_t count = sizeof(keys) / sizeof(const char *);
    for (size_t f1 = 0; f1 != count; ++f1)
    {
        for (size_t f2 = 0; f2 != count; ++f2)
        {
            for (size_t f3 = 0; f3 != count; ++f3)
            {
                std::string value;
                param.clear();
                param["f1"] = keys[f1];
                param["f2"] = keys[f2];
                param["f3"] = keys[f3];

                value
                    .append(keys[f1])
                    .append(keys[f2])
                    .append(keys[f3]);

                result = pTree->Add(param, value);
            }
        } 
    }

    result = pTree->Update();
    return 0;
}



TEST(testStarTree, compileTest)
{
    std::vector<std::string> fieldList;
    fieldList.push_back("f1");
    fieldList.push_back("f2");
    fieldList.push_back("f3");

    std::string defaultKey = "*";

    StarTree<std::string, 
        std::string, std::string>  starTree(fieldList, defaultKey, "NULL", "");

    starTree.DumpFieldInfo(std::cout);
}


TEST(testStarTree, addTest1)
{
    const char * keys[] = {
        "1",
        "2",
        "3",
    };

    std::vector<std::string> fieldList;
    fieldList.push_back("f1");
    fieldList.push_back("f2");
    fieldList.push_back("f3");

    std::string defaultKey = "*";

    StarTree<std::string, 
        std::string, std::string>  starTree(fieldList, defaultKey, "NULL", "");

    std::map<std::string, std::string> param;
    int result = 0;

    size_t count = sizeof(keys) / sizeof(const char *);
    for (size_t f1 = 0; f1 != count; ++f1)
    {
        for (size_t f2 = 0; f2 != count; ++f2)
        {
            for (size_t f3 = 0; f3 != count; ++f3)
            {
                std::string value;
                param.clear();
                param["f1"] = keys[f1];
                param["f2"] = keys[f2];
                param["f3"] = keys[f3];

                value
                    .append(keys[f1])
                    .append(keys[f2])
                    .append(keys[f3]);

                result = starTree.Add(param, value);
                EXPECT_EQ(0, result);
            }
        } 
    }

    result = starTree.Update();
    EXPECT_EQ(0, result);

    param.clear();
    param["f1"] = "1";
    param["f2"] = "1";
    result = starTree.Add(param, "fail");
    EXPECT_EQ(StarTreeError::ERROR_INVALID_PARAM, result);

    param.clear();
    result = starTree.Add(param, "fail");
    EXPECT_EQ(StarTreeError::ERROR_INVALID_PARAM, result);

    starTree.DumpKeyInfo(std::cout);
    starTree.DumpTree(std::cout);
   
}

TEST(testStarTree, mergeTest1)
{
    const char * keys[] = {
        "1",
        "2",
        "*",
    };

    std::vector<std::string> fieldList;
    fieldList.push_back("f1");
    fieldList.push_back("f2");
    fieldList.push_back("f3");

    std::string defaultKey = "*";

    StarTree<std::string, std::string, std::string>  starTree(fieldList, defaultKey, "NULL", "");

    std::map<std::string, std::string> param;
    int result = 0;

    size_t count = sizeof(keys) / sizeof(const char *);
    for (size_t f1 = 0; f1 != count; ++f1)
    {
        for (size_t f2 = 0; f2 != count; ++f2)
        {
            for (size_t f3 = 0; f3 != count; ++f3)
            {
                std::string value;
                param.clear();
                param["f1"] = keys[f1];
                param["f2"] = keys[f2];
                param["f3"] = keys[f3];

                value
                    .append(acapella::StringUtil::ToString(f1))
                    .append(acapella::StringUtil::ToString(f2))
                    .append(acapella::StringUtil::ToString(f3));


                result = starTree.Add(param, value);
                EXPECT_EQ(0, result);
            }
        } 
    }
    starTree.DumpKeyInfo(std::cout);
    result = starTree.Update();
    EXPECT_EQ(0, result);

    starTree.DumpTree(std::cout);
    printf("after merge\n");

    result = starTree.MergeLimit("100");
    EXPECT_EQ(0, result);
    starTree.DumpTree(std::cout);
   
}

/*
TEST(testStarTree, iteratorTest)
{
    Tree * pTree = NULL;

    BuildTestTree(pTree);

    pTree->DumpTree();

    delete pTree;
}


TEST(testStarTree, iterator2Test)
{
    std::vector<std::string> fieldList;
    fieldList.push_back("f1");
    fieldList.push_back("f2");
    fieldList.push_back("f3");

    std::string defaultKey = "*";

    StarTree<std::string, 
        std::string, std::string>  starTree(fieldList, defaultKey, "NULL", "");

    for (StarTree<std::string, std::string, std::string>::iterator it = starTree.begin();
         it != starTree.end();
         ++it)
    {
        std::cout << acapella::StringUtil::Implode(it->GetParam(starTree), "-") << ":\t" 
                  << it->GetValue() << std::endl;
    }
}

TEST(testStarTree, iterator3Test)
{
    std::vector<std::string> fieldList;

    std::string defaultKey = "*";

    StarTree<std::string, 
        std::string, std::string>  starTree(fieldList, defaultKey, "NULL", "");

    for (StarTree<std::string, std::string, std::string>::iterator it = starTree.begin();
         it != starTree.end();
         ++it)
    {
    }
}

TEST(testStarTree, iterator4Test)
{
    Tree * pTree = NULL;

    BuildTestTree(pTree);

    std::map<std::string, std::string> param;
    param["f1"] = "1";
    param["f2"] = "*";
    Tree::TreeNodePtr pNode = pTree->Get(param);

    for (StarTree<std::string, std::string, std::string>::iterator it(pNode);
         it != pTree->end();
         ++it)
    {
    }

    delete pTree;
}


TEST(testStarTree, getTest1)
{
    const char * keys[] = {
        "1",
        "2",
        "3",
    };

    std::map<std::string, std::string> param;

    Tree * pTree = NULL;

    BuildTestTree(pTree);
 
    size_t count = sizeof(keys) / sizeof(const char *);
    for (size_t f1 = 0; f1 != count; ++f1)
    {
        for (size_t f2 = 0; f2 != count; ++f2)
        {
            for (size_t f3 = 0; f3 != count; ++f3)
            {
                std::string value;
                param.clear();
                param["f1"] = keys[f1];
                param["f2"] = keys[f2];
                param["f3"] = keys[f3];

                value
                    .append(keys[f1])
                    .append(keys[f2])
                    .append(keys[f3]);

                Tree::TreeNodePtr pNode = pTree->Get(param);
                EXPECT_EQ(true, pNode != NULL);
                EXPECT_EQ(value, pNode->GetValue());
            }
        } 
    }

    param.clear();
    param["f1"] = "1";
    param["f2"] = "2";
    param["f3"] = "4";
    const Tree::TreeNode * pNode = pTree->Get(param);
    EXPECT_EQ(true, pNode != NULL);

    delete pTree;
}


TEST(testStarTree, getTest2)
{
    const char * keys[] = {
        "1",
        "2",
        "3",
        "NULL"
    };

    std::map<std::string, std::string> param;

    Tree * pTree = NULL;

    BuildTestTree(pTree);
 
    size_t count = sizeof(keys) / sizeof(const char *);
    for (size_t f1 = 0; f1 != count; ++f1)
    {
        for (size_t f2 = 0; f2 != count; ++f2)
        {
            for (size_t f3 = 0; f3 != count; ++f3)
            {
                std::string value;
                param.clear();
                param["f1"] = keys[f1];
                param["f2"] = keys[f2];
                param["f3"] = keys[f3];

                value
                    .append(keys[f1])
                    .append(keys[f2])
                    .append(keys[f3]);

                Tree::TreeNodePtr pNode = pTree->Get(param);
                EXPECT_EQ(true, pNode != NULL);
                EXPECT_EQ(value, pNode->GetValue());
            }
        } 
    }

    param.clear();
    param["f1"] = "1";
    param["f2"] = "2";
    param["f3"] = "4";
    Tree::TreeNodePtr pNode = pTree->Get(param);
    EXPECT_EQ(true, pNode->GetValue() == "12NULL");

    param["f1"] = "1";
    param["f2"] = "2";
    param["f3"] = "";
    pNode = pTree->Get(param);
    EXPECT_EQ(true, pNode->GetValue() == "122");

    delete pTree;
}


TEST(testStarTree, deleteTest1)
{

    Tree * pTree = NULL;

    BuildTestTree(pTree);

    std::map<std::string, std::string> param;
    param["f1"] = "1";

    int result = pTree->Delete(param);
    EXPECT_EQ(0, result);
    pTree->Update();
    pTree->DumpTree();

    delete pTree;
}
    */
    
TEST(testStarTree, EffeciencyTest)
{
    const char * keys[] = {
        "0",
        "1",
        "2",
        "3",
        "4",
        "5",
        "6",
        "7",
        "8",
        "9",
        "10",
        "11",
        "12",
        "13",
        "14",
        "15",
        "16",
        "17",
        "18",
        "19",
        "20",
            /*
        "21",
        "22",
        "23",
        "24",
        "25",
        "26",
        "27",
        "28",
        "29",
        "30",
        "31",
        "32",
        "33",
            */
        "NULL",
        "*"
    };

    std::vector<std::string> fieldList;
    fieldList.push_back("f1");
    fieldList.push_back("f2");
    fieldList.push_back("f3");
    fieldList.push_back("f4");
    fieldList.push_back("f5");

    std::string defaultKey = "*";

    Tree * pTree = new Tree(fieldList, defaultKey, "NULL", "");

    std::map<std::string, std::string> param;
    int result = 0;

    size_t count = sizeof(keys) / sizeof(const char *);
    size_t sum = 0;
    for (size_t f1 = 0; f1 != count; ++f1)
    {
        for (size_t f2 = 0; f2 != count; ++f2)
        {
            for (size_t f3 = 0; f3 != count; ++f3)
            {
                for (size_t f4 = 0; f4 != count; ++f4)
                {
                    for (size_t f5 = 0; f5 != count; ++f5)
                    {
                        std::string value;
                        param.clear();
                        param["f1"] = keys[f1];
                        param["f2"] = keys[f2];
                        param["f3"] = keys[f3];
                        param["f4"] = keys[f4];
                        param["f5"] = std::string(keys[f5]) + "12345678901234567890";

                        value
                            .append(keys[f1])
                            .append(keys[f2])
                            .append(keys[f3])
                            .append(keys[f4])
                            .append(keys[f5])
                            .append("01234567890123456789");

                        result = pTree->Add(param, value);
                        ++sum;
                    }
                }
            }
            std::cout << "f2:" << f2 << std::endl;
        } 
        std::cout << "f1:" << f1 << std::endl;
    }

    std::cout << "start update" << std::endl
              << "node count:" << sum << std::endl;

    result = pTree->Update();
    result = pTree->MergeLimit("123");
    std::cout << time(NULL) << std::endl;

    /*
    for (size_t f1 = 0; f1 != count; ++f1)
    {
        for (size_t f2 = 0; f2 != count; ++f2)
        {
            for (size_t f3 = 0; f3 != count; ++f3)
            {
                for (size_t f4 = 0; f4 != count; ++f4)
                {
                    for (size_t f5 = 0; f5 != count; ++f5)
                    {
                        param["f1"] = keys[f1];
                        param["f2"] = keys[f2];
                        param["f3"] = keys[f3];
                        param["f4"] = keys[f4];
                        param["f5"] = keys[f4];


                        Tree::TreeNodePtr pNode = pTree->Get(param);
                        //EXPECT_EQ(true, pNode != NULL);
                    }
                }
            }
        } 
    }
*/
    while (1) {}

    std::cout << time(NULL) << std::endl;
}

